/**
  ******************************************************************************
  * @file    ring_buffer.c
  * @author  ashuai0110
  * @version V2.4
  * @date    2024-09-10
  * @brief   环形缓冲区
  *          (适合定长或易分离出的数据,提供覆写函数和非覆写函数)
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  * Change Logs:
  * Date          Author        Notes
  * 2023-09-24    ashuai0110    完成基本内容
  * 2023-12-26    ashuai0110    修改读写接口取有效数量读写
  * 2024-06-11    ashuai0110    增加覆写函数和头尾节点偏移读写函数
  * 2024-08-16    ashuai0110    修改非覆写函数仍会覆写的BUG,优化读写逻辑,读写时增加临界区控制
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "ring_buffer.h"

/**
 * @addtogroup modules
 * @{
 */

/**
 * @defgroup ring_buffer ring_buffer
 * @{
 */

/* 私有宏定义-----------------------------------------------------------------*/
/**
 * @defgroup ring_buffer_local_macros ring buffer local macros
 * @{
 */

/**
 * @defgroup ring_buffer_check_parameters_validity ring buffer check parameters validity
 * @{
 */
/* 环形缓冲区数据存储区大小有效性检查 */
#define IS_VALID_RB_SIZE(x)                                                   \
(       (x) != 0u)
/**
 * @}
 */

/**
 * @}
 */

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/

/**
 * @defgroup ring_buffer_global_functions ring buffer global functions
 * @{
 */

/**
  * @brief  环形缓冲区初始化
  *
  * @param  _rb    : 环形缓冲区实例
  *
  * @param  pBuf   : 数据缓冲区
  *
  * @param  bufLen : 数据缓冲区长度byte
  *
  * @retval None
  */
void ring_buffer_init(ring_buffer_t *_rb, void *pBuf, uint32_t bufLen)
{
    MW_ASSERT_PARAM(IS_VALID_POINTER(_rb));
    MW_ASSERT_PARAM(IS_VALID_POINTER(pBuf));
    MW_ASSERT_PARAM(IS_VALID_RB_SIZE(bufLen));
    
    /* 初始化各参数 */
    _rb->pBuf = (char *)pBuf;
    _rb->pHead = _rb->pBuf;
    _rb->pTail = _rb->pBuf;
    _rb->bufLen = bufLen;
    _rb->rwFlag = 0;
}

/**
  * @brief  环形缓冲区取消初始化
  *
  * @note   恢复默认
  *
  * @param  _rb : 环形缓冲区实例
  *
  * @retval None
  */
void ring_buffer_de_init(ring_buffer_t *_rb)
{
    /* 置零各参数 */
    memset(_rb, 0, sizeof(ring_buffer_t));
}

/**
  * @brief  获取环形缓冲区的可读数量
  *
  * @note   待读出数量byte
  *
  * @param  _rb : 环形缓冲区实例
  *
  * @retval 返回环形缓冲区的可读数量
  */
uint32_t ring_buffer_can_read(ring_buffer_t *_rb)
{
    /* 读节点大于写节点则总长度-(读节点-写节点) */
    if(_rb->pHead > _rb->pTail)
    {
        return (_rb->bufLen - (_rb->pHead - _rb->pTail));
    } /* 读节点小于写节点则写节点-读节点 */
    else if(_rb->pHead < _rb->pTail)
    {
        return (_rb->pTail - _rb->pHead);
    }
    /* 读节点等于写节点则根据读写标志返回 */
    return _rb->rwFlag ? _rb->bufLen : 0;
}

/**
  * @brief  获取环形缓冲区的可写数量
  *
  * @note   可写入数量byte
  *
  * @param  _rb : 环形缓冲区实例
  *
  * @retval 返回环形缓冲区的可写数量
  */
uint32_t ring_buffer_can_write(ring_buffer_t *_rb)
{
    /* 总长度减去可读数量既是可写数量 */
    return (_rb->bufLen - ring_buffer_can_read(_rb));
}

/**
  * @brief  从环形缓冲区读出数据
  *
  * @param  _rb  : 环形缓冲区实例
  *
  * @param  pBuf : 读出数据的存储区
  *
  * @param  len  : 读出数据的数量byte
  *
  * @retval 返回实际读出数据数量byte
  */
uint32_t ring_buffer_read(ring_buffer_t *_rb, void *pBuf, uint32_t len)
{
    uint32_t readSize = 0;

    MW_CRITICAL_LOCK();
    /* 读出数量超出可读数量则按可读数量进行读出 */
    len = (len > ring_buffer_can_read(_rb)) ? ring_buffer_can_read(_rb) : len;
    if(len)
    {
        /* 读节点大于等于写节点则说明写入数据溢出过则读取需拼接数据 */
        if(_rb->pHead >= _rb->pTail)
        {
            /* 不计入溢出的数量还剩多少可读出数据 */
            readSize = _rb->bufLen - (_rb->pHead - _rb->pBuf);
            /* 若读出数量大于剩余可读出数量则需将剩余数量读完再从头节点读出剩下的 */
            if(len > readSize)
            {
                memcpy(pBuf, _rb->pHead, readSize);
                _rb->pHead = _rb->pBuf;
                pBuf = (void *)((char *)pBuf + readSize);
                len -= readSize;
            }
            else
            {
                readSize = 0;
            }
        }
        memcpy(pBuf, _rb->pHead, len);
        _rb->pHead += len;
        readSize += len;
        /* 写入数量已满则将写节点赋值为头节点 */
        if(_rb->pHead >= (_rb->pBuf + _rb->bufLen))
        {
            _rb->pHead = _rb->pBuf;
        }
        _rb->rwFlag = 0;
    }
    MW_CRITICAL_UNLOCK();

    return readSize;
}

/**
  * @brief  写入数据到环形缓冲区(写满后无法继续写入)
  *
  * @param  _rb   : 环形缓冲区实例
  *
  * @param  pData : 写入数据的缓冲区
  *
  * @param  len   : 写入数据的数量byte
  *
  * @retval 返回实际写入数据数量byte
  */
uint32_t ring_buffer_write(ring_buffer_t *_rb, void *pData, uint32_t len)
{
    uint32_t writeSize = 0;

    MW_CRITICAL_LOCK();
    /* 写入数量超出可写数量则按可写数量进行写入 */
    len = (len > ring_buffer_can_write(_rb)) ? ring_buffer_can_write(_rb) : len;
    if(len)
    {
        /* 读节点小于等于写节点则说明写入数据可以溢出从读节点写则可能需分段写入 */
        if(_rb->pHead <= _rb->pTail)
        {
            /* 不考虑溢出的数量还可以写入多少 */
            writeSize = _rb->bufLen - (_rb->pTail - _rb->pBuf);
            /* 若写入数量大于剩余可写数量则先将剩余写入 */
            if(len > writeSize)
            {
                memcpy(_rb->pTail, pData, writeSize);
                _rb->pTail = _rb->pBuf;
                pData = (void *)((char *)pData + writeSize);
                len -= writeSize;
            }
            else
            {
                writeSize = 0;
            }
        }
        memcpy(_rb->pTail, pData, len);
        _rb->pTail += len;
        writeSize += len;
        /* 写入数量已满则将写节点赋值为头节点 */
        if(_rb->pTail >= (_rb->pBuf + _rb->bufLen))
        {
            _rb->pTail = _rb->pBuf;
        }
        _rb->rwFlag = 1;
    }
    MW_CRITICAL_UNLOCK();

    return writeSize;
}

/**
  * @brief  覆盖写入数据到环形缓冲区(写满后继续写入则覆写旧数据)
  *
  * @param  _rb   : 环形缓冲区实例
  *
  * @param  pData : 写入数据的缓冲区
  *
  * @param  len   : 写入数据的数量byte
  *
  * @retval 返回实际写入数据数量byte
  */
uint32_t ring_buffer_overwrite(ring_buffer_t *_rb, void *pData, uint32_t len)
{
    uint32_t writeSize = 0;

    MW_CRITICAL_LOCK();
    /* 写入数量超出可写数量则覆盖写入 */
    if(len > ring_buffer_can_write(_rb))
    {
        writeSize = _rb->bufLen - (_rb->pHead - _rb->pBuf);
        /* 写入数量超出剩余数量则覆写剩余并从头节点继续覆写 */
        if(len > writeSize)
        {
            _rb->pHead = _rb->pBuf + len - writeSize;
        } /* 否则则直接覆写剩余 */
        else
        {
            _rb->pHead += len;
        }
    }
    /* 读节点小于等于写节点则说明写入数据可以溢出从读节点写则可能需分段写入 */
    if(_rb->pHead <= _rb->pTail)
    {
        /* 不考虑溢出的数量还可以写入多少 */
        writeSize = _rb->bufLen - (_rb->pTail - _rb->pBuf);
        /* 若写入数量大于剩余可写数量则先将剩余写入 */
        if(len > writeSize)
        {
            memcpy(_rb->pTail, pData, writeSize);
            _rb->pTail = _rb->pBuf;
            pData = (void *)((char *)pData + writeSize);
            len -= writeSize;
        }
        else
        {
            writeSize = 0;
        }
    }
    memcpy(_rb->pTail, pData, len);
    _rb->pTail += len;
    writeSize += len;
    /* 写入数量已满则将写节点赋值为头节点 */
    if(_rb->pTail >= (_rb->pBuf + _rb->bufLen))
    {
        _rb->pTail = _rb->pBuf;
    }
    _rb->rwFlag = 1;
    MW_CRITICAL_UNLOCK();
    
    return writeSize;
}

/**
  * @brief  环形缓冲区设置头节点(开始读节点)偏移
  *
  * @param  _rb    : 环形缓冲区实例
  *
  * @param  offset : 偏移量
  *
  * @retval None
  */
void ring_buffer_set_read_offset(ring_buffer_t *_rb, uint32_t offset)
{
    _rb->pHead = _rb->pBuf + offset;
}

/**
  * @brief  环形缓冲区获取头节点(开始读节点)偏移
  *
  * @param  _rb    : 环形缓冲区实例
  *
  * @retval 返回头节点(开始读节点)偏移量
  */
uint32_t ring_buffer_get_read_offset(ring_buffer_t *_rb)
{
    return _rb->pHead - _rb->pBuf;
}

/**
  * @brief  环形缓冲区设置尾节点(开始写节点)偏移
  *
  * @param  _rb    : 环形缓冲区实例
  *
  * @param  offset : 偏移量
  *
  * @retval None
  */
void ring_buffer_set_write_offset(ring_buffer_t *_rb, uint32_t offset)
{
    _rb->pTail = _rb->pBuf + offset;
}

/**
  * @brief  环形缓冲区获取尾节点(开始写节点)偏移
  *
  * @param  _rb    : 环形缓冲区实例
  *
  * @retval 返回尾节点(开始写节点)偏移量
  */
uint32_t ring_buffer_get_write_offset(ring_buffer_t *_rb)
{
    return _rb->pTail - _rb->pBuf;
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */
